package com.wonderland.lucy.security.shiro;

import com.wonderland.lucy.jedis.JedisTempalte;
import com.wonderland.lucy.util.SerializeUtils;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.util.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

public class RedisCache<K, V> implements Cache<K, V> {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    private JedisTempalte jedisTempalte;

    private String keyPrefix = JedisSessionDao.KEY_PREFIX;

    public void setKeyPrefix(String keyPrefix) {
        this.keyPrefix = keyPrefix;
    }

    /**
     * 通过一个JedisManager实例构造RedisCache
     */
    public RedisCache(JedisTempalte jedisTempalte) {
        if (jedisTempalte == null) {
            throw new IllegalArgumentException("jedisTempalte argument cannot be null.");
        }
        this.jedisTempalte = jedisTempalte;
    }

    public RedisCache(JedisTempalte jedisTempalte, String prefix) {
        this(jedisTempalte);
        this.keyPrefix = prefix;
    }

    /**
     * 获得byte[]型的key
     *
     */
    private byte[] getByteKey(K key) {
        if (key instanceof String) {
            String preKey = this.keyPrefix + key;
            return preKey.getBytes();
        } else {
            return SerializeUtils.serialize(key);
        }
    }

    @Override
    public V get(final K key) throws CacheException {
        logger.debug("根据key从Redis中获取对象 key [" + key + "]");
        try {
            if (key == null) {
                return null;
            } else {
                byte[] rawValue = jedisTempalte.execute(jedis -> {
                    return jedis.get(getByteKey(key));
                });
                @SuppressWarnings("unchecked")
                V value = (V) SerializeUtils.deserialize(rawValue);
                return value;
            }
        } catch (Throwable t) {
            throw new CacheException(t);
        }

    }

    @Override
    public V put(final K key, final V value) throws CacheException {
        logger.debug("根据key从存储 key [" + key + "]");
        try {
            jedisTempalte.execute(jedis -> {
                jedis.set(getByteKey(key), SerializeUtils.serialize(value));
            });
            return value;
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }

    @Override
    public V remove(final K key) throws CacheException {
        logger.debug("从redis中删除 key [" + key + "]");
        try {
            V previous = get(key);
            jedisTempalte.execute(jedis -> {
                jedis.del(getByteKey(key));
            });
            return previous;
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }

    @Override
    public void clear() throws CacheException {
        logger.debug("从redis中删除所有元素");
        try {
            keys().forEach(this::remove);
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }

    @Override
    public int size() {
        try {
            return keys().size();
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public Set<K> keys() {
        try {
            Set<byte[]> keys = jedisTempalte.execute(jedis -> {
                return jedis.keys((keyPrefix + "*").getBytes());
            });
            if (CollectionUtils.isEmpty(keys)) {
                return Collections.emptySet();
            } else {
                return (Set<K>) keys;
            }
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }

    @Override
    public Collection<V> values() {
        try {
            Set<byte[]> keys = jedisTempalte.execute(jedis -> {
                return jedis.keys((keyPrefix + "*").getBytes());
            });
            if (!CollectionUtils.isEmpty(keys)) {
                List<V> values = new ArrayList<>(keys.size());
                for (byte[] key : keys) {
                    @SuppressWarnings("unchecked")
                    V value = get((K) key);
                    if (value != null) {
                        values.add(value);
                    }
                }
                return Collections.unmodifiableList(values);
            } else {
                return Collections.emptyList();
            }
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }

}
